# -*- coding: utf-8 -*-
"""
Created on 2025.4.9 10:00
@author: tanglengfo
"""

'''
面向对象编程——Object Oriented Programming，简称OOP，是一种程序设计思想。OOP把对象作为程序的基本单元，一个对象包含了数据和操作数据的函数。
在Python中，所有数据类型都可以视为对象，当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类（Class）的概念。
'''

# 自定义一个类，用于表示学生成绩
class Student(object): # 类名通常以大写字母开头，多个单词之间使用驼峰命名法，如StudentGrade，StudentScore等。
    def __init__(self, name, score): # 类的构造函数，用于初始化对象的属性。
        self.name = name # self是类的实例，name是类的属性。
        self.score = score # score是类的属性。
    def print_score(self): # 类的方法，用于打印学生的成绩。
        print('%s: %s' % (self.name, self.score)) # 打印学生的姓名和成绩。
    
# 创建一个学生对象
zhangsan = Student('zhangsan', 99) # 创建一个学生对象，姓名为zhangsan，成绩为99。
zhangsan.print_score() # 调用学生对象的print_score方法，打印学生的成绩。
lishi = Student('lishi', 98) # 创建一个学生对象，姓名为lishi，成绩为98。
lishi.print_score() # 调用学生对象的print_score方法，打印学生的成绩。


'''
一个上帝创造人的实例
'''

# 定义一个人类
# class 类 (人)的模型， instance 实例 (你,我,他，张三，李四...) ,会有些属性(姓名，性别，身高,年龄,体重等) ，还会有些技能(吃饭,泡妞,说话,走路,跳高等)
class Person: # 类名通常以大写字母开头，多个单词之间使用驼峰命名法，如StudentGrade，StudentScore等。
    
    def __init__(self, name, sex,age, hight): 
        '''
        构造函数，用于初始化对象人有哪些的属性。
        参数：
            name: 姓名
            sex: 性别
            age: 年龄
            hight: 身高
        '''
        self.name = name # self是类的实例，name是类的属性。姓名
        self.sex = sex # 性别。
        self.age = age # 年龄。
        self.hight = hight # 身高属性。
        
    def print_info(self): # 人拥有的技能，显示自己的属性信息。
        print('%s: %s, %s, %s' % (self.name, self.sex, self.age, self.hight)) # 显示自己的所有属性信息。
    
    def eat(self): # 类的方法，创造出来这个人吃的技能。
        print('%s: %s' % (self.name, '这个人吃啊吃！')) # 使用技能吃。
        
    def paoniu(self): # 类的方法，创造出这个人泡妞的技能。
        if self.sex == '男': # 如果性别是男，
            print('%s: %s' % (self.name, 'boy 去 paoniu')) # 让男人去泡妞。
        else:
            print('%s: %s' % (self.name, 'girl 可不能 paoniu')) # 提示女人没此技能。
    
  
        
        
# 创造一个男人
zhangsan = Person('zhangsan', '男', 18, 175) # 创建一个人，姓名为zhangsan，性别为男，年龄为18，身高为175。
zhangsan.print_info() # 调用人对象的print_info方法，显示此人的信息。
zhangsan.eat() # 调用此人的eat方法，执行此人的技能。
zhangsan.paoniu() # 调用人对象的paoniu方法，如男人就去泡妞。

# 创造一个女人
lisi = Person('lisi', '女', 19, 165) # 创建一个人，姓名为lisi，性别为女，年龄为19，身高为165。
lisi.print_info() # 调用此人的print_info方法，显示此人的属性。
lisi.eat() # 调用她的eat方法，让她去吃。
lisi.paoniu() # 调用此女人的paoniu方法，被提示不能去paoniu。


# 定义一个动物类
class Animal(object):
    cry = {'猫': '喵喵叫', '狗': '汪汪叫', '鸟': '鸟叫', '牛': '哞哞叫', 
           '狼': '嗷嗷叫', '羊': '咩咩叫', '人': '啊啊啊啊的叫'}
    
    def __init__(self, name):
        self.__name = name  # 使用双下划线表示私有属性
        
    @property
    def name(self):
        return self.__name
        
    def said(self):
        sound = self.cry.get(self.__name, '没有收录此动物的技能')
        print(sound)

class Cat(Animal):
    def __init__(self, name, *colors):
        super().__init__(name)
        self.colors = list(colors)  # 使用复数形式表示多个颜色
        print(f'我是一只{self.name}，我的颜色是{self.colors[0]}')
        
    def said(self):
        sound = self.cry[self.name]
        print(f'我是一只{self.name}，我的颜色是{self.colors}，我会{sound}')
        
    def __str__(self):
        return f'我是一只{self.name}，我的颜色是{self.colors}'

# 测试代码
animal1 = Animal('狗')
animal1.said()

animal2 = Cat('猫', '黑色', 'black')
animal2.said()
animal2.colors = ['白色']  # 修改颜色属性
print(animal2)



# 继承和多态
'''
继承是面向对象编程中的一个重要概念，它允许一个类（称为子类或派生类）继承另一个类（称为父类或基类）的属性和方法。
子类可以继承父类的所有属性和方法，并且可以添加自己的属性和方法，也可以重写父类的方法。

'''
class Anim(object):   #编写Animal类
    def run(self):
        print("Animal is running...")

class Dog(Anim):  #Dog类继承Amimal类，没有run方法
    '''
    Dog class的说明信息,继承自Anim类，
    本身没有run方法
    '''
    pass

class Cat1(Anim):  #Cat类继承Animal类，有自己的run方法
    def run(self):
        print('Cat is running...')
    pass

class Car(object):  #Car类不继承，有自己的run方法
    def run(self):
        print('Car is running...')

class Stone(object):  #Stone类不继承，也没有run方法
    pass

def run_twice(animal):
    animal.run()
    

run_twice(Anim())
run_twice(Dog())
run_twice(Cat1())
run_twice(Car())
# run_twice(Stone())    #报错，Stone类没有run方法，无法调用run方法


# 判断对象类型，使用type()函数：

# 对于class的继承关系来说，使用type()就很不方便。我们要判断class的类型，可以使用isinstance()函数。
print(isinstance(123, int)) # True
print(isinstance(Dog(), Anim)) # True
print(isinstance(Dog(), object)) # True  所有类都继承自object类，所以所有类都是object类的子类。
print(isinstance(Cat1(), Anim)) # True
print(isinstance(Car(), Anim)) # False
print(isinstance(Stone(), Anim)) # False
print(isinstance(Stone(), object)) # True
# isinstance语句中，class类型，要加（）才是类实例，不加（）是类类型。

d = dir(Dog()) # dir()函数可以获得一个对象的所有属性和方法，返回一个包含字符串的list，比如，获得一个str对象的所有属性和方法：
print(d)
print(Car)  # 打印Car类的类型，输出：<class '__main__.Car'>，表示Car类的类型是__main__.Car。
print(Dog().__doc__)  # 使用对象方法打印Dog类的文档字符串，输出：Dog class的说明信息。


# 仅仅把属性和方法列出来是不够的，配合getattr()、setattr()以及hasattr()，我们可以直接操作一个对象的状态：
# 测试该对象的属性：hasattr(obj, 'x')  # 有属性'x'吗？
# 获得该对象的属性：getattr(obj, 'x') # 获取属性'x'
# 设置该对象的属性：setattr(obj, 'y', 19) # 设置属性'y'
# 如果试图获取不存在的属性，会抛出AttributeError的错误：
# 可以传入一个default参数，如果属性不存在，就返回默认值：getattr(obj, 'z', 404) # 获取属性'z'，如果不存在，返回默认值404
# 也可以获得对象的方法：getattr(obj, 'power') # 获取方法'power'
# 也可以获得对象的方法：getattr(obj, 'power')() # 获取方法'power'并调用
# 注意：只有在不知道对象信息的时候，我们才会去获取对象信息。如果可以直接写：obj.x，就不要写：getattr(obj, 'x')。


def dict2bean(obj, dic):
    for k, v in dic.items():
        if hasattr(obj, k): # 判断obj对象是否有k属性
            # setattr(obj, k, v) # 设置obj对象的k属性为v
            obj.k = v  #这里无法修改类中a,d的值，使用setattr(obj,k,v)就可以
                       # 因为这里obj.k是给实例化对象添加一个属性，而不是修改类中a,d的值。
                       # 所以如想按字典中数据给类实例对象相对应的属性赋值，要使用setattr(obj,k,v)就可以修改类中a,d的值。

class obj():
    d=1
    def __init__(self,a):
        self.a=a


dic={'a':3,'d':88}
x=obj(12)   # 实例化对象，为什么可以传参数？因为__init__方法是初始化方法，在实例化对象时会自动调用，所以可以传参数。
            # 可以不传参，但是__init__方法必须有一个参数，否则会报错。
dict2bean(x, dic)

print(x.a)   # 12
print(x.d)   # 1   这里在运行过程中，没有修改类中a,d的值，所以这里输出的是类中a,d的值。



# 为了统计学生人数，可以给Student类增加一个类属性，每创建一个实例，该属性自动增加：
class Student1(object):
    '''
    Student.count += 1 --> 是对Student这个类的count进行加1操作,每使用一次这个类，就会对count进行加1操作，
    但是这个类是所有实例共享的属性，所以每次使用这个类，都会对count进行加1操作，
    self.count += 1 --> 因在__init__函数中，每次实例出对象，则执行一次此语句，实例的count进行加1操作，无法实现累计，下一个实例的count还是从0开始加1   
    '''
    
    count = 0 # 类属性，用于统计学生人数，类属性是所有实例共享的属性。

    def __init__(self, name):
        self.name = name # 实例属性，用于存储学生的姓名。
        Student1.count += 1 # 每创建一个实例，类属性count加1，这里也可以使用self.count += 1，但是这样会出错，因为self.count是实例属性，而不是类属性。

# 测试:
if Student1.count != 0:
    print('测试失败!')
else:
    bart = Student1('Bart') # 创建一个学生实例，姓名为Bart。
    if Student1.count != 1: # 类属性count应该为1。
        print('测试失败!')
    else:
        lisa = Student1('Lisa') # 创建一个学生实例，姓名为Lisa。
        if Student1.count != 2: # 类属性count应该为2。
            print('测试失败!')
        else:
            print('Students:', Student1.count)
            print('测试通过!')
